package raft.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Builder;
import org.rocksdb.Options;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
import raft.entity.LogEntry;
import raft.service.LogModule;

import java.io.File;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class LogModuleImpl implements LogModule {


    private static RocksDB logDb;
    public final static byte[] LAST_INDEX_KEY = "LAST_INDEX_KEY".getBytes();
    ReentrantLock lock = new ReentrantLock();

    //No Know....
    public static String dbDir;
    public static String logsDir;

    static {
        if (dbDir == null) {
            dbDir = "./rocksDB-raft/" + new Random().nextInt(10);
        }
        if (logsDir == null) {
            logsDir = dbDir + "/logModule";
        }
        RocksDB.loadLibrary();
    }


    public LogModuleImpl(){
        Options options = new Options();
        options.setCreateIfMissing(true);

        File file = new File(logsDir);
        boolean success = false;
        if (!file.exists()) {
            file.mkdirs();
        }

        try {
            logDb = RocksDB.open(options, logsDir);
        } catch (RocksDBException e) {
          e.printStackTrace();
        }
    }

    public static LogModuleImpl getInstance(){return DefaultLogsLazyHolder.INSTANCE;}

    private static class DefaultLogsLazyHolder{
        private static final LogModuleImpl INSTANCE = new LogModuleImpl();
    }

    @Override
    public void write(LogEntry logEntry) {

        boolean success = false;

        try {
            lock.tryLock(3000, TimeUnit.MILLISECONDS);
            logEntry.setIndex(getLastIndex()+1);
            logDb.put(logEntry.getIndex().toString().getBytes(), JSON.toJSONBytes(logEntry));
            success = true;

        }catch (Exception e){


           e.printStackTrace();
        }
        finally {
            if (true){
              updateLastIndex(logEntry.getIndex());
            }
            lock.unlock();
        }


    }

    private void updateLastIndex(Long index) {

        try {
           logDb.put(LAST_INDEX_KEY,index.toString().getBytes());

        }catch (Exception e){
            e.printStackTrace();
        }


    }

    @Override
    public LogEntry read(Long index) {
        try {
            byte[]log =  logDb.get(index.toString().getBytes());
            if(log == null){
                return null ;
            }
        return  JSONObject.parseObject(log,LogEntry.class);
        }catch (Exception e){
            e.printStackTrace();
        }


        return null;
    }

    @Override
    public void removeOnStarIndex(Long startIndex) {

        boolean success = false;
            try {
                lock.tryLock(3000,TimeUnit.MILLISECONDS);

                for(long i = startIndex;i<=getLastIndex();i++){
                    logDb.delete(String.valueOf(i).getBytes());

                }
                success = true;


            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(success){
                    updateLastIndex(startIndex);
                }
                lock.unlock();
            }


    }

    @Override
    public LogEntry getLast() {
        try {
          return  read(getLastIndex());
        }catch (Exception e){
            e.printStackTrace();
        }


        return null;
    }

    @Override
    public Long getLastIndex() {
        byte[] lastindex = "-1".getBytes();

        try {
            lastindex = logDb.get(LAST_INDEX_KEY);
            if(lastindex == null){
                lastindex = "-1".getBytes();
            }

        }catch (Exception e){
            e.printStackTrace();
        }


        return Long.valueOf(new String(lastindex));
    }
}
